//用户数据库模块：负责管理用户账号信息，包括注册、登录、密码修改和用户删除等功能。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <pthread.h>
#include "user_db.h"
#include "config.h"
#include "utils.h"

pthread_mutex_t user_db_mutex;// 用户数据库互斥锁

/**
 *  初始化用户数据库相关资源。
 * 
 * 该函数负责初始化用户数据库操作所需的互斥锁，以保证多线程环境下对用户数据库操作的线程安全。
 * 同时，确保用户数据库文件存在，如果文件不存在则创建该文件。
 */
void init_user_db() {
  
    //&user_db_mutex 要初始化的互斥锁
    //NULL 互斥锁的属性，通常为 NULL，表示使用默认属性
    pthread_mutex_init(&user_db_mutex, NULL);  // 初始化互斥锁
    // 以追加模式打开用户数据库文件
    // 若文件不存在，会自动创建该文件；若文件已存在，不会清空文件内容，而是在文件末尾追加新数据。
    FILE *db = fopen(USER_DB_FILE, "a"); //返回值：成功--指向db的指针；失败--返回NULL
    // 检查文件是否成功打开 
    if (db) {
        // 若文件成功打开，关闭文件，确保资源被正确释放
        printf("文件打开成功\n");
        fclose(db);
    }else{
        // 若文件打开失败，打印错误信息并退出程序
        perror("Error opening user database file");
        exit(1);
    }
}

/**
 *  注册新用户到用户数据库。
 * 
 * 该函数会先检查用户名是否已存在于用户数据库中，若存在则返回 0。
 * 若用户名不存在，则将新用户信息（用户名和密码）添加到数据库文件中。
 * 在操作数据库文件时使用互斥锁保证线程安全。
 * 
 *   username 要注册的用户名，以 null 结尾的字符串。
 *   password 要注册的用户密码，以 null 结尾的字符串。
 *  return int 注册结果状态码：
 *         - 1: 注册成功；
 *         - 0: 用户名已存在；
 *         - -1: 打开用户数据库文件失败。
 */
int register_user(const char *username, const char *password) {
    // 加锁，确保在访问用户数据库文件时的线程安全
    pthread_mutex_lock(&user_db_mutex);
    
    // 检查用户名是否已存在
    // 以只读模式打开用户数据库文件（文件必须存在）
    FILE *db = fopen(USER_DB_FILE, "r");
    // 若文件成功打开，开始检查用户名
    if (db) {
        // 定义缓冲区，用于存储从文件中读取的每一行内容
        char line[256];
        // 逐行读取用户数据库文件，直到文件结束
        while (fgets(line, sizeof(line), db)) {
            // 定义两个缓冲区，分别用于存储从当前行解析出的用户名和密码
            char saved_user[50], saved_pass[50];

            // 从当前行中解析出用户名和密码，格式为 "用户名:密码"
            //%49[^:] 最多匹配49个非':'字符，用于存储用户名
            //%49s 最多匹配49个任意字符，用于存储密码
            if (sscanf(line, "%49[^:]:%49s", saved_user, saved_pass) == 2) {
                // 比较解析出的用户名和传入的用户名是否相同（用户名是否匹配）
                if (strcmp(saved_user, username) == 0) {
                    // 若用户名已存在，关闭文件，解锁并返回 0
                    fclose(db);
                    pthread_mutex_unlock(&user_db_mutex);
                    return 0; // 用户名已存在
                }
            }
        }
        // 关闭已打开的用户数据库文件
        fclose(db);
    }
    
    // 添加新用户
    // 以追加模式打开用户数据库文件
    db = fopen(USER_DB_FILE, "a");
    // 若文件打开失败,db=NULL，解锁并返回 -1
    if (!db) {
        pthread_mutex_unlock(&user_db_mutex);
        return -1; // 打开文件失败
    }
    
    // 将新用户的用户名和密码以 "用户名:密码" 的格式写入文件，并换行
    //fprintf函数：将格式化的数据写入db文件中
    //db：要写入的文件指针，指向要写入的文件
    fprintf(db, "%s:%s\n", username, password);
    // 关闭已打开的用户数据库文件
    fclose(db);
    
    // 解锁，允许其他线程访问用户数据库文件
    pthread_mutex_unlock(&user_db_mutex);
    // 返回 1 表示注册成功
    return 1; // 注册成功
}

/**
 *  用户登录函数，验证用户名和密码是否匹配。
 * 
 * 该函数会从用户数据库文件中查找指定用户名，并验证对应的密码是否正确。
 * 在操作数据库文件时使用互斥锁保证线程安全。
 * 
 *  username 要验证的用户名，以 null 结尾的字符串。
 *  password 要验证的密码，以 null 结尾的字符串。
 * return int 登录结果状态码：
 *         - 1: 登录成功；
 *         - 0: 密码错误；
 *         - -1: 用户数据库文件不存在或用户不存在。
 */
int login_user(const char *username, const char *password) {
    // 加锁，确保在访问用户数据库文件时的线程安全
    pthread_mutex_lock(&user_db_mutex);
    
    // 以只读模式打开用户数据库文件
    FILE *db = fopen(USER_DB_FILE, "r");
    // 检查文件是否成功打开，若失败则解锁并返回 -1
    if (!db) {
        pthread_mutex_unlock(&user_db_mutex);
        return -1; // 文件不存在
    }
    
    // 定义缓冲区，用于存储从文件中读取的每一行内容
    char line[256];
     // 逐行读取用户数据库文件，直到文件结束
    while (fgets(line, sizeof(line), db)) {
        // 定义两个缓冲区，分别用于存储从当前行解析出的用户名和密码
        char saved_user[50], saved_pass[50];
        // 从当前行中解析出用户名和密码，格式为 "用户名:密码"
        if (sscanf(line, "%49[^:]:%49s", saved_user, saved_pass) == 2) {
            // 比较解析出的用户名和传入的用户名是否相同
            //左>右--返回一个小于0的整数
            //左<右--返回一个大于0的整数
            //左==右--返回0
            if (strcmp(saved_user, username) == 0) {
                
                // 比较解析出的密码和传入的密码是否相同
                if (strcmp(saved_pass, password) == 0) {
                    // 密码匹配，关闭文件，解锁并返回 1 表示登录成功
                    fclose(db);
                    pthread_mutex_unlock(&user_db_mutex);
                    return 1; // 登录成功
                } else {
                    // 密码不匹配，关闭文件，解锁并返回 0 表示密码错误
                    fclose(db);
                    pthread_mutex_unlock(&user_db_mutex);
                    return 0; // 密码错误
                }
            }
        }
    }
    
    // 关闭已打开的用户数据库文件
    fclose(db);
    // 解锁，允许其他线程访问用户数据库文件
    pthread_mutex_unlock(&user_db_mutex);
    // 若未找到指定用户名，返回 -1 表示用户不存在
    return -1; // 用户不存在
}

/**
 *  销毁用户数据库互斥锁。
 * 
 * 该函数用于销毁用户数据库操作时使用的互斥锁，释放相关资源。   
 * 在程序结束或不再需要使用该互斥锁时调用此函数，以避免资源泄漏。
 */
void destroy_user_db_mutex() {
    // 调用 pthread_mutex_destroy 函数销毁用户数据库互斥锁，释放其占用的系统资源
    pthread_mutex_destroy(&user_db_mutex);
}